package inutan.game;

import bright.serialization.ByteBuf;
import inutan.common.constant.DataConstant;
import inutan.common.context.FlowContext;
import inutan.common.kit.InnerKit;
import inutan.common.kit.PackIdKit;
import inutan.common.message.ActionErrorEnum;
import inutan.common.inner.protocol.InnerMessage;
import inutan.game.business.ActorManager;
import inutan.game.business.GameFlowContext;
import luban.common.LongValue;
import org.ricks.common.TaskBus;
import org.ricks.common.exception.MsgException;
import org.ricks.common.lang.Logger;
import org.ricks.ioc.RicksContext;
import org.ricks.ioc.definition.wrap.ActionMethodWrapper;
import org.ricks.ioc.definition.wrap.RpcMethodWrapper;
import org.ricks.net.*;
import org.ricks.net.handler.AbstractMessageProcessor;
import org.ricks.net.rpc.AskPackage;

import java.io.IOException;
import java.util.Objects;

/**
 * @author chenwei
 * @Description: 玩家消息 处理handler
 * 1、维护玩家session
 * 2、维护不同模块节点session
 * 3、转发给其它模块节点
 * 4、转发给玩家 （to 1, to N, to all ）
 * @date 2023/2/1510:40
 */
public class GameHandler extends AbstractMessageProcessor<Object> {

    @Override
    public void process0(AioSession session, Object obj) {
        /**
         * broker玩家消息 处理handler 不需要玩家路由 action
         * 直接cmd 进行转发模块，避免功能开发繁琐
         * 需要根据cmd 做不同的处理
         * 尽量简化 broker 指令 职责，做好一次开发以后尽量不用动
         */
        if(obj instanceof InnerMessage msg) {
            //功能模块开启，web gm 功能关闭开启
            ActionMethodWrapper methodDefinition = RicksContext.me().getActionMethod(msg.cmd);
            if (Objects.isNull(methodDefinition)) {
                Logger.error("路由错误 cmd:" + msg.cmd);
                return;
            }
            DefaultPacket defaultPacket = new DefaultPacket(msg.cmd, msg.data);
            GameFlowContext flowContext = new GameFlowContext(session, defaultPacket);
            synchronized (flowContext) {
                flowContext.setUserSessionId(msg.userSessionId); //用户sessionId 玩家未登录绑定
                flowContext.setPackageId(msg.packageId);
                flowContext.setUserId(msg.userId);
                /**
                 * 玩家都在固定的actor执行业务逻辑
                 * 登录逻辑 单独一个线程处理？
                 */

                invoke(flowContext, methodDefinition);
                actionAfter(flowContext); //如果业务存在自定义异常，就直接推送给玩家
            }
        } else if(obj instanceof AskPackage pack) {
            RpcMethodWrapper rpcMethodWrapper = RicksContext.me().getRpcHandler(pack.getCmd());
            if (Objects.isNull(rpcMethodWrapper)) {
                Logger.error("Rpc 路由错误 cmd:" + pack.getCmd());
                return;
            }
            switch (pack.getCmd()) {
                case 5000 -> {
                    LongValue value = LongValue.deserializeLongValue(new ByteBuf(pack.getPacket()));
                    ActorManager.get().getUseQuitAction().execute("user-quit", () -> {
                        AskPackage packet = (AskPackage) rpcMethodWrapper.invoke(pack);
                        session.send(packet);
                    });
                }
                case 30000 -> {
                    TaskBus.execute(pack, () -> {
                        AskPackage packet = (AskPackage) rpcMethodWrapper.invoke(pack);
                        session.send(packet);
                    });
                }
                default ->  System.err.println("未知RPC指令");
            }
        }
    }

    /**
     * 执行action method ，铺抓异常
     * @param context
     * @param methodDefinition
     * @return
     */
    private void invoke(final FlowContext<DefaultPacket> context,ActionMethodWrapper methodDefinition) {
        try {
            methodDefinition.invoke(context);
            context.writeAndFlush();
        } catch (Throwable e) {
            context.setError(true);
            int merge = PackIdKit.merge(context.getPackageId(),(byte) 1);
            context.setPackageId(merge);

            if (e instanceof MsgException msgException) {
                DefaultPacket defaultPacket = context.getPacket();
                Logger.info("路由出现内部错误 cmd:"+defaultPacket.getCmd()+" ,内部错误 error code:"+msgException.getMsgCode()+" and case:" + msgException.getMessage());
                context.setMethodResult(msgException);
                return;
            }

            // 到这里，一般不是用户自定义的错误，很可能是开发者引入的第三方包或自身未捕获的错误等情况
            Logger.error("系统异常：" +e.toString());
            e.printStackTrace();
            context.setMethodResult(new MsgException(ActionErrorEnum.systemOtherErrCode));
        }
    }

    private void actionAfter(final FlowContext<DefaultPacket> context) {
        // 业务方法的返回值
        final Object result = context.getMethodResult();

        // 如果有异常错误，异常处理
        if (context.isError()) {

            MsgException msgException = (MsgException) result;
            short code = msgException.getMsgCode();
            DefaultPacket defaultPacket = context.getPacket();
            InnerMessage message = InnerKit.createInnerMessage(defaultPacket.getCmd());
            message.userId = context.getUserId();
            message.userSessionId = context.getUserSessionId();
            message.state = DataConstant.BROADCAST_MESSAGE;
            message.responseStatus = code;
            message.data = new byte[0];
            context.sendResponse(message);
        }
    }


    @Override
    public void stateEvent0(AioSession session, StateMachineEnum stateMachineEnum, Throwable throwable) {
        switch (stateMachineEnum) {
            case NEW_SESSION:
                try {
                    Logger.info(session.getLocalAddress() + ":create session " + session.getRemoteAddress());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                break;
            case PROCESS_EXCEPTION:
                Logger.error("process exception", throwable);
//                session.close();
                break;
            case SESSION_CLOSED:
                Logger.info("Gateway stop & close session.......  sessionId: " +session.sid());
                break;
            case DECODE_EXCEPTION: {
                Logger.warn("Game decode exception,", throwable);
                break;
            }
        }
    }
}
